﻿

using System;
using System.Data;
using System.Linq;
using System.Reflection;
using Autofac;
using Casamiel.API.Application;
using Casamiel.API.Application.Commands;
using Casamiel.API.Application.Services;
using Casamiel.API.Infrastructure.Behaviors;
using Casamiel.API.Infrastructure.Middlewares;
using Casamiel.Application;
using Casamiel.Application.Commands;
using Casamiel.Application.Validations;
using Casamiel.Infrastructure.Repositories;
using FluentValidation;
using MediatR;
using Smooth.IoC.Repository.UnitOfWork;
using Smooth.IoC.UnitOfWork.Interfaces;

namespace Casamiel.API.Infrastructure.AutofaceModules
{
    /// <summary>
    /// Application module.
    /// </summary>
    public class ApplicationModule : Autofac.Module
    {
        /// <summary>
        /// Gets the connection string.
        /// </summary>
        /// <value>The connection string.</value>
        public string ConnectionString { get; }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="qconstr"></param>
        public ApplicationModule(string qconstr)
        {
            ConnectionString = qconstr;
        }
        /// <summary>
        /// Load the specified builder.
        /// </summary>
        /// <returns>The load.</returns>
        /// <param name="builder">Builder.</param>
		protected override void Load(ContainerBuilder builder)
        {
            Register(builder);
        }
        /// <summary>
        /// Register the specified builder.
        /// </summary>
        /// <returns>The register.</returns>
        /// <param name="builder">Builder.</param>
		public void Register(ContainerBuilder builder)
        {
            builder.Register(c => new CasaMielSession(c.Resolve<IDbFactory>(), ConnectionString)).As<Casamiel.Application.ICasaMielSession>();


            builder.RegisterType<DbFactory>().As<IDbFactory>();
            builder.RegisterGeneric(typeof(Repository<,>)).As(typeof(IRepository<,>));
            builder.RegisterType<Smooth.IoC.UnitOfWork.UnitOfWork>().As<IUnitOfWork>();

            builder.RegisterType<Application.Services.IcApiService>().As<Application.Services.IIcApiService>().SingleInstance();

            builder.RegisterType<Application.Services.PayGateWayService>().As<IPayGateWayService>().InstancePerLifetimeScope();
            builder.RegisterType<AllPayService>().As<IAllPayService>().InstancePerLifetimeScope();
            builder.RegisterType<RefundService>().As<Application.IRefundService>().InstancePerLifetimeScope();
            builder.RegisterType<MobileCheckCodeService>().As<IMobileCheckCode>().InstancePerLifetimeScope();
            builder.RegisterType<TokenProvider>().As<ITokenProvider>().InstancePerLifetimeScope();
            builder.RegisterType<StoreApiService>().As<IStoreApiService>().SingleInstance();

            builder.RegisterType<StockService>().As<IStockService>().SingleInstance();
            
           

            builder.RegisterType<MallService>().As<IMallService>().InstancePerLifetimeScope();
            builder.RegisterAssemblyTypes(typeof(MemberRepository).Assembly).Where(t => t.Name.EndsWith("Repository", StringComparison.CurrentCulture)).AsImplementedInterfaces().InstancePerLifetimeScope();
            builder.RegisterAssemblyTypes(typeof(MemberCardService).Assembly).Where(t => t.Name.EndsWith("Service", StringComparison.CurrentCulture)).AsImplementedInterfaces().InstancePerLifetimeScope();


            // Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
            builder.RegisterAssemblyTypes(typeof(IMediator).GetTypeInfo().Assembly).AsImplementedInterfaces();

            builder.RegisterAssemblyTypes(typeof(CreateICConsumeCodeCommand).GetTypeInfo().Assembly)
                .AsClosedTypesOf(typeof(IRequestHandler<,>));
            var mediatrOpenTypes = new[]
            {
                typeof(IRequestHandler<,>),
                typeof(INotificationHandler<>),
            };

            foreach (var mediatrOpenType in mediatrOpenTypes) {
                builder
                    .RegisterAssemblyTypes(typeof(BindMemberCardCommandHandler).GetTypeInfo().Assembly)
                    .AsClosedTypesOf(mediatrOpenType)
                    .AsImplementedInterfaces();
            }
            builder
               .RegisterAssemblyTypes(typeof(CreateICConsumeCodeCommandValidator).GetTypeInfo().Assembly)
               .Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
               .AsImplementedInterfaces();
            builder.Register<ServiceFactory>(context => {
                var componentContext = context.Resolve<IComponentContext>();
                return t => { return componentContext.TryResolve(t, out object o) ? o : null; };
            });
            builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
            builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
        }

        /// <summary>
        /// Db factory.
        /// </summary>
		sealed class DbFactory : IDbFactory
        {
            private readonly IComponentContext _container;
            public DbFactory(IComponentContext container)
            {
                _container = container;
            }
            /// <summary>
            /// Create this instance.
            /// </summary>
            /// <returns>The create.</returns>
            /// <typeparam name="T">The 1st type parameter.</typeparam>
			public T Create<T>() where T : class, ISession
            {
                return _container.Resolve<T>();
            }
            /// <summary>
            /// Create the specified isolationLevel.
            /// </summary>
            /// <returns>The create.</returns>
            /// <param name="isolationLevel">Isolation level.</param>
            /// <typeparam name="TUnitOfWork">The 1st type parameter.</typeparam>
            /// <typeparam name="TSession">The 2nd type parameter.</typeparam>
			public TUnitOfWork Create<TUnitOfWork, TSession>(IsolationLevel isolationLevel = IsolationLevel.Serializable) where TUnitOfWork : class, IUnitOfWork where TSession : class, ISession
            {
                return _container.Resolve<TUnitOfWork>(new NamedParameter("factory", _container.Resolve<IDbFactory>()),
                    new NamedParameter("session", Create<TSession>()), new NamedParameter("isolationLevel", isolationLevel)
                    , new NamedParameter("sessionOnlyForThisUnitOfWork", true));
            }
            /// <summary>
            /// Create the specified factory, session and isolationLevel.
            /// </summary>
            /// <returns>The create.</returns>
            /// <param name="factory">Factory.</param>
            /// <param name="session">Session.</param>
            /// <param name="isolationLevel">Isolation level.</param>
            /// <typeparam name="T">The 1st type parameter.</typeparam>
			public T Create<T>(IDbFactory factory, ISession session, IsolationLevel isolationLevel = IsolationLevel.Serializable) where T : class, IUnitOfWork
            {
                return _container.Resolve<T>(new NamedParameter("factory", factory),
                    new NamedParameter("session", session), new NamedParameter("isolationLevel", isolationLevel));
            }
            /// <summary>
            /// Release the specified instance.
            /// </summary>
            /// <returns>The release.</returns>
            /// <param name="instance">Instance.</param>
			public void Release(IDisposable instance)
            {
                instance.Dispose();
            }
        }
    }
}
